//五种模式
/**
 * 1.引入自己编写的模块
 * require('./prev.js')
 * 2.引入第三方模块
 * const md5=require('md5')
 * console.log(md5('123'))
 * 3.nodejs内置模块
 *  fs http ner os child_process
 * const fs=require('node:fs')//高版本nodejs node:fs 低版本 fs
 * console.log(fs)
 * 4.c++ addon napi node-gyp .node
 * 5.引入json文件
 * const data=require('./data.json')
 * console.log(data);
 */
// const data = require("./data.json");
// console.log(data);
//引入模块
// const data = require("./test.js");
// const { success } = require("./test.js");
// console.log("data: ", data, success);
//引入函数或值
//commonjs方式导入 "type": "commonJS",
// const num = require("./test.js");
// console.log("num: ", num());
//import方式导入 "type": "module",
//第一种写法
// import obj, { name as aliasName } from "./test1.js";
// console.log("success: ", obj, aliasName); //支持别名
//第二种写法 查看导出所有
// import * as all from "./test1.js";
// console.log(all);
//esm不支持引入json文件 nodejs18高版本支持需要改变写法
// import json from "./data.json" assert { type: "json" };
// console.log("json: ", json);

// console.log(__dirname, filename);//版本需要高
// console.log(process.argv); // node index.js --xm --ddd 经常在cli命令中获取参数 是一个数组
//seo
// const fs = require("node:fs");
// const { JSDOM } = require("jsdom");

// const dom = new JSDOM(`<!DOCTYPE html><div id='app'></div>`);
// const document = dom.window.document;
// const window = dom.window;
// //fetch node 18版半后才有的用法  和浏览器一样
// fetch("https://api.thecatapi.com/v1/images/search?limit=10&page=1")
//   .then((res) => res.json())
//   .then((data) => {
//     const app = document.getElementById("app");
//     data.forEach((item) => {
//       const img = document.createElement("img");
//       img.src = item.url;
//       img.style.width = "200px";
//       img.style.height = "200px";
//       app.appendChild(img);
//     });
//     console.log(dom.serialize()); //转为字符串
//     //第一个参数是文件路径，第二个参数是要写入的内容
//     fs.writeFileSync("./index.html", dom.serialize());
//   });
//以上操作属于SSR服务端渲染
//CSR 客户端渲染 vue react spa（单页应用）
//path
// const path = require("node:path");
// //1.basename 返回给定路径的最后一部分 xs.html
// //window系统 默认是F:/project/node
// //window兼容正斜杠写法
// console.log(path.basename("C:/temp/myfile.html")); //myfile.html
// console.log(path.basename(__filename)); //index.js
// //posix
// console.log(path.win32.basename("C:\\temp\\myfile.html")); //C:\temp\myfile.html 需要使用win32才能正确返回myfile.html
// //2.dirname 返回给定路径的目录名
// //window
// console.log(path.dirname("F:/aaaa/bbbb/cccc/index.html", 1)); //F:/aaaa/bbbb/cccc
// //posix
// console.log(path.win32.dirname("F:\\aaaa\\bbbb\\cccc\\index.html", 2)); //F:\aaaa\bbbb\cccc

// //3.extname 返回给定路径的文件扩展名  .html 如果没有扩展名返回空字符串  如果有多个扩展名返回最后一个
// console.log(path.extname("F:/aaaa/bbbb/cccc/index.html")); //.html
// console.log(path.extname("F:/aaaa/bbbb/cccc/index")); //空值
// console.log(path.extname("F:/aaaa/bbbb/cccc/index.a.a.a.a.html")); //.html
// //4.path.join 连接路径  支持操作符../
// console.log(path.join("F:/a/b", "/c")); //F:/a/b/c
// console.log(path.join("F:/a/b/c", "../")); //F:/a/b
// console.log(path.win32.join("F:/a/b", "/c")); //F:\a\b\c
// //5.path.resolve 解析路径 返回一个绝对路径
// //如果都是绝对路径返回最后一个
// //如果只有一个相对路径返回挡墙工作目录的绝对路径
// console.log(path.resolve("/a", "/b", "/c")); // /c
// console.log(path.win32.resolve("/a", "/b", "/c")); // \c
// console.log(path.resolve("./index.js")); // /home/user1/文档/owner-project/小满/xiaoman-nodejs-package/index.js
// console.log(path.resolve(__dirname, "./index.js")); // /home/user1/文档/owner-project/小满/xiaoman-nodejs-package/index.js
// //6.path.parse 解析路径 返回一个对象 path.format逆向操作 互补的操作
// console.log(path.parse("/a/b/c"));
// // {
// //     root: '/',根目录
// //     dir: '/a/b',文件所在目录
// //     base: 'c',文件名+后缀名
// //     ext: '',后缀名
// //     name: 'c'文件名
// // }
// //7.path.format 格式化路径
// console.log(path.format({ root: "/", dir: "/a/b/", base: "c" })); // /a/b/c
// //8. path.sep 路径分隔符  window返回的是\  posix返回的是/
// console.log(path.sep);
//os
// nodejs os模块可以跟操作系统进行交互
// const os = require("node:os");
// //1.platfrom 返回操作系统类型 win32 windows darwin mac linux
// console.log(os.platform()); //linux
// //2,realease 返回操作系统版本号
// console.log(os.release()); //6.5.0-28-generic
// //3.type 返回操作系统的类型
// console.log(os.type()); //Linux
// //4.version
// console.log(os.version()); //#29~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Apr  4 14:39:20 UTC 2
// const os = require("node:os");
// //exec可以执行shell命令
// const { exec } = require("child_process");
// const platform = os.platform();
// const open = (url) => {
//   //mac
//   if (platform === "darwin") {
//     exec(`open ${url}`);
//   } else if (platform === "win32") {
//     //window
//     exec(`start ${url}`);
//   } else if (platform === "linux") {
//     //linux
//     exec(`xdg-open ${url}`);
//   }
// };
// open("https://www.baidu.com");
//获取用户目录 底层原理 windows %userprofile% mac $HOME
// console.log(os.homedir()); ///home/user1
// //cpu架构
// console.log(os.arch()); //x64
// //操作系统 线程cpu信息
// console.log(os.cpus().length);
//使用场景  cpu利用率
//[
//   {
//     model: 'Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz',//cpu型号
//     speed: 2000,//cpu速度
//     times: {
//      user: 469000,//用户态时间
//      nice: 0,//低优先级时间
//      sys: 10000,//内核态时间
//      idle: 1000000,//空闲时间
//      irq: 0 //硬件中断时间
//     }//cpu使用情况
//   },
//网络信息
// console.log(os.networkInterfaces()); //获取网络信息
/**
 * {
      address: '127.0.0.1',//ip地址
      netmask: '255.0.0.0',//子网掩码
      family: 'IPv4',//ip版本 ipv4 6
      mac: '00:00:00:00:00:00',//mac地址
      internal: true,//是否是内网地址
      cidr: '127.0.0.1/8'//ip地址段
    },
 */
// //内存使用情况
// const mem = os.freemem();
// const totalMem = os.totalmem();
// console.log(`内存使用率${(mem / totalMem) * 100}%`);
// console.log(process.arch); //x64
// console.log(process.platform); //linux
//进程的参数
// console.log(process.argv);
/**
 * [
  '/snap/node/7581/bin/node', //使用的什么去执行脚本
  '/home/user1/文档/owner-project/小满/xiaoman-nodejs-package/index.js',//运行的文件
  '--version'//参数
]
 */
//可以自定义参数 --version
// 执行命令 node index.js --version
// console.log(process.argv.includes("--version") ? "1.0.0" : "无"); //1.0.0
// 执行命令 node index.js
// console.log(process.argv.includes("--version") ? "1.0.0" : "无"); //无
//cwd 获取工作目录 __dirname 获取当前文件所在目录效果相同
//区别 esm模式下不能使用__dirname 可以使用cwd代替
// console.log(process.cwd(), __dirname); ///home/user1/文档/owner-project/小满/xiaoman-nodejs-package
//内存信息
// console.log(process.memoryUsage());
// {
//  rss: 24693760, // 进程使用的内存大小
//  heapTotal: 18565120, //  v8分配的堆内存总大小
//  heapUsed: 10043936, // 已经使用的堆内存大小
//  external: 16458, // 外部内存大小 c++使用的
//  arrayBuffers: 16458 // 数组缓冲区大小 二进制总量
// }
//exit 退出进程
// setTimeout(() => {
//   console.log(5);
// }, 5000);

// setTimeout(() => {
//   process.exit();
// }, 2000);
// process.on("exit", () => {
//   console.log("进程退出了");
// });
//kill杀死进程 需要一个pid 进程id
// setTimeout(() => {
//   console.log(5);
// }, 5000);

// setTimeout(() => {
//   process.kill(process.pid);
// }, 2000);
//env 环境变量 获取操作系统所有的环境变量 允许修改 但只在当前进程中生效，不会真正影响系统环境变量
// console.log(process.env);
//可以安装一个插件 cross-env 跨平台设置环境变量
// console.log(process.env.NODE_ENV == "dev" ? "开发环境" : "生产环境");
/**
 * 原理
 * 跨平台的 windows SET 环境变量 posix export 设置环境变量
 */
//child_process 创建子进程
//commonjs 模块化 引入
// const {
//   exec, //执行shell命令
//   execSync,
//   spawn,
//   spawnSync,
//   execFile,
//   execFileSync,
//   fork,
// } = require("child_process");
//module 模块化 引入
// import { exec, execSync } from "node:child_process";
// import path from "node:path";
//1.exec 异步的方法 会有回调函数返回一个buffer 可以帮我们执行shell命令 或者跟软件交互
//2.execSync 同步的方法
//适合执行较小的shell命令  比如立马拿到结果的shell execSync exec的字节上线是2000，超出报错
// exec("node -v", (err, stdout, stderr) => {
//   if (err) {
//     return err;
//   }
//   console.log(stdout.toString());
// });

// const nodeVersion = execSync("node -v").toString();
// console.log(nodeVersion); //v14.15.0
//创建文件夹 的shell命令
// execSync("mkdir test"); //创建一个名为test的文件夹
// execSync("xdg-open  http://www.baidu.com");
//跟软件交互 打开软件
// execSync("F:\\music\\QQMusic\\QQMusic.exe --params");

//3.spwan 创建子进程 没有字节上线 返回的是流并且是实时的
//4.spwanSync 用的比较少
//可以通过第二个参数来传递参数 第三个参数是配置项
// const { stdout } = spawn("netstat", ["-a"]，{
//     cwd <string> 子进程的当前工作目录。
//     env <Object> 环境变量键值对。
//     encoding <string> 默认为 'utf8'。
//     shell <string> 用于执行命令的 shell。 在 UNIX 上默认为 '/bin/sh'，在 Windows 上默认为 process.env.ComSpec。 详见 Shell Requirements 与 Default Windows Shell。
//     timeout <number> 默认为 0。
//     maxBuffer <number> stdout 或 stderr 允许的最大字节数。 默认为 200*1024。 如果超过限制，则子进程会被终止。 查看警告： maxBuffer and Unicode。
//     killSignal <string> | <integer> 默认为 'SIGTERM'。
//     uid <number> 设置该进程的用户标识。（详见 setuid(2)）
//     gid <number> 设置该进程的组标识。（详见 setgid(2)）
// });
// stdout.on("data", (msg) => {
//   console.log(msg.toString()); //打印出所有的网络信息
// });

// stdout.on("close", () => {
//   console.log("结束了");
// });

//5.execFile 异步的方法 执行可执行文件
/**
 * window bat.cmd
 * linux xx.sh
 */

// execFile(path.resolve(__dirname, "./bat.cmd"), null, (err, stdout) => {
//   console.log(stdout.toString());
// });
//底层实现顺序
// exec->execFile ->spwan
//6.execFileSync 同步的方法
//7.fork 只能接收js文件，帮js创建子进程
//nodejs的弱点是不适合做cpu密集型应用，适合做io密集型应用 把耗时的代码放到子进程中执行，子进程执行完再通知主进程
//父子进程相互通信
// const testProcess = fork("./test.js");
// testProcess.send("我是主进程");
// testProcess.on("message", (msg) => {
//   console.log(msg + "，ok,干得不错"); //我是子进程
// });
/**
 * fork底层原理
 * 是通过ipc进行通信，ipc是基于libuv实现，libuv分别调用不同操作系统的底层api
 * windows names pipe
 * posix(macos linux):unix domain socket(套节字)
 */
//功能需求 mp4生成gif图片
// execSync("ffmpeg -i cat.mp4 cat1.gif", { stdio: "inherit" }); //stdio: "inherit" 输出执行过程的参数
// execSync("ffmpeg -i cat.mp4 cat.avi", { stdio: "inherit" });
//功能需求 提取视频中的音频 即生成mp3文件
// execSync("ffmpeg -i cat.mp4 cat.mp3", { stdio: "inherit" });
//裁剪视频
// execSync("ffmpeg -ss 2 -to 5  -i cat.mp4 cat2.mp4", { stdio: "inherit" });
//添加水印 -vf drawtext=text="CQJ":fontsize=30:x=10:y=10:fontcolor=white
// execSync(
//   'ffmpeg -i cat.mp4 -vf drawtext=text="CQJ":fontsize=30:x=10:y=10:fontcolor=white cat3.mp4',
//   { stdio: "inherit" }
// );
//删除水印 -vf delogo=w=90:y=30:x=10:y=10
// execSync("ffmpeg -i cat.mp4 -vf delogo=w=90:y=30:x=10:y=10 cat4.mp4", {
//   stdio: "inherit",
// });
//events 用法跟mitt用法类似 都是发布订阅模式
// const eventsEmitter = require("events");
// //发布订阅模式 off on emit once
// /**
//  *
//  */
// const bus = new eventsEmitter();
// //订阅一个事件，事件名称，回调  事件默认最多监听10个，否则报错，除非设置 bus.setMaxListeners(20)
// console.log(bus.getMaxListeners()); //默认10
// bus.on("test", (name) => {
//   console.log(name);
// });
// //once 只能订阅一次
// bus.once("test1", (name) => {
//   console.log(name);
// });
// //off
// const fn = () => {
//   console.log("off");
// };
// bus.on("test", fn);
// bus.off("test", fn);
// //发布一个事件，事件名称，参数
// bus.emit("test", "我是事件");
// bus.emit("test", "我是事件");
// bus.emit("test1", "我是事件1");
// bus.emit("test1", "我是事件1");
// // process底层也是使用的events 嫁接了events的原型到process

// import util from "node:util";
// import { exec } from "node:child_process";
// exec("node -v", (err, stdout, stderr) => {
//   console.log(111, stdout);
//   if (err) {
//     return err;
//   }
//   console.log(stdout);
// });

// const execPromise = util.promisify(exec); //返回一个新的函数
//如果返回多个参数 res是对象 如果一个参数则直接返回
// execPromise("node -v")
//   .then((res) => {
//     console.log(res);
//   })
//   .catch((err) => {
//     console.log(err);
//   });
//实现promisify  剖析原理
// const promisify = (fn) => {
//   return (...args) => {
//     return new Promise((resolve, reject) => {
//       fn(...args, (err, ...rest) => {
//         if (err) {
//           reject(err);
//         }
//         if (rest && rest.length > 1) {
//           let obj = {};
//           for (let key in rest) {
//             obj[key] = rest[key];
//           }
//           resolve(obj);
//         } else {
//           resolve(rest[0]);
//         }
//       });
//     });
//   };
// };
// const execPromise = promisify(exec);
// execPromise("node -v")
//   .then((res) => {
//     console.log(res);
//     //拿不到stdout 和stderr util.promisify内部使用SymbolkCustomPromisifyArgsSymbol，但没暴露出来，所以无法拿到
//   })
//   .catch((err) => {
//     console.log(err);
//   });
// const fn = (type) => {
//   if (type === 1) return Promise.resolve("sucess");
//   return Promise.reject("error");
// };
// //自己实现callbackify
// const callbackify_self = (fn) => {
//   return (...args) => {
//     //回调函数是最后一个参数
//     let callback = args.pop();
//     fn(...args)
//       .then((res) => {
//         callback(null, res);
//       })
//       .catch((err) => callback(err));
//   };
// };
// const callback = callbackify_self(fn);
// callback(1, (err, res) => {
//   console.log(err, res);
// });
//c语言 printf %s 匹配字符 对应第一个参数
// util.format("%s---%s", "cqj", "nb");
// console.log("util.format", util.format("%s---%s", "cqj", "nb"));

//--output 指定输出文件名
//quality 指定压缩质量 0-100 越大图片越大效果越好
//压缩处理指定文件夹下的图片  处理签118kb 处理后32.6kb
// const imageFolder = "./images";
// exec(
//   `pngquant ${path.join(
//     imageFolder,
//     "404.png"
//   )} --quality=100 --output ${path.join(imageFolder, "test_404.png")}`
// );
// import fs from "node:fs";
// import fs2 from "node:fs/promises";
//异步 同步 promise
//1.异步 不加sync就是异步
// fs.readFile(
//   "./index.txt",
//   {
//     encoding: "utf-8",
//     flag: "r", //读
//   },
//   (err, data) => {
//     if (err) throw err;
//     console.log(data);
//   }
// );
// //2.同步 此方式会阻塞代码，等到读取完成才会执行
// const res = fs.readFileSync("./index.txt"); //此时返回的是一个buffer 二进制流 可以使用1., { encoding: "utf-8" } 2.res.toString('utf-8')
// console.log("res: ", res.toString());
// console.log("readFileSync执行完才到我");
// // 3.promise
// fs2.readFile("./index.txt").then((res) => {
//   console.log(res.toString("utf-8"));
// });
// const readStream = fs.createReadStream("./index.txt");
// readStream.on("data", (chunk) => {
//   console.log(chunk.toString());
// });
// readStream.on("end", () => {
//   console.log("文件读取完成");
// });
//创建文件夹

// fs.mkdirSync("./cqj-folder/cqj-folder2/cqj-folder3", { recursive: true });
//删除文件
// fs.rmSync("./cqj-folder", { recursive: true });
//重命名文件
// fs.renameSync("./index.txt", "index1.txt");
//监听文件变化

// fs.watch("index1.txt", (eventType, filename) => {
//   console.log("eventType, filename: ", eventType, filename);
// });
//写入文件
// fs.writeFileSync("./index.txt", "\n vue之父 鱿鱼须", {
//   flag: "ax", //append 追加
// });
//追加文件
// fs.appendFileSync("./index.txt", "\n 2021年1月1日");
//创建可写流 大量数据分批插入
// let verse = ["带到秋来酒吧月下", "夜深人静", "望明月", "高处不胜寒"];
// let writeStream = fs.createWriteStream("./index.txt");
// verse.forEach((item) => {
//   writeStream.write(item + "\n");
// });
// writeStream.end();
// writeStream.on("finish", () => {
//   console.log("写入完成");
// });
//软链接 硬链接
//硬链接 硬链接后的地址共享文件 备份文件
// fs.linkSync("./index.txt", "./index4.txt");
//软链接 箱windows的快捷方式 需要管理员权限
// fs.symlinkSync("./index.txt", "./index5.txt");
const crypto = require("node:crypto");
//对称加密 双方协商定义一个密钥及iv
/**
 * 第一个参数 algorithm 接收一个算法
 * 第二个参数  key 接收一个密钥 32位
 * 第三个参数  iv 初始化向量 16位 保证生成的密钥串每次是不一样的，密钥串缺少位数可以补码
 */
// let key = crypto.randomBytes(32);
// let iv = Buffer.from(crypto.randomBytes(16));
// const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
// cipher.update("cqj", "utf-8", "hex");
// const result = cipher.final("hex"); // 输出16进制密文
// console.log(result);
// //解密 使用相同的算法，密钥和初始化向量解密
// const de = crypto.createDecipheriv("aes-256-cbc", key, iv);
// de.update(result, "hex", "utf-8");
// console.log("de: ", de.final("utf-8"));
//非对称加密
//生成公钥私钥
//私钥只能是管理员拥有 ，不能对外公开
//公钥可以公开
// const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
//   modulusLength: 1024, // 密钥长度越长越安全 越慢
// });
// const encrypted = crypto.publicEncrypt(publicKey, Buffer.from("cqj1111"));
// console.log("encrypted: ", encrypted.toString("hex"));
// const decrypted = crypto.privateDecrypt(privateKey, encrypted);
// console.log("decrypted: ", decrypted.toString());
//哈希函数 不能被解密 因为他说单向的 不可逆
let hash = crypto.createHash("sha256"); //md5
hash.update("cqj===");
console.log('hash.digest("hex"): ', hash.digest("hex"));
